package GameServer;

import Library.*;
import Library.Game.*;
import Library.Login.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Array;
import java.rmi.*;
import java.util.*;
import java.util.Map.*;

/**
 * Game representeert het spel dat gespeeld kan worden door spelers.
 */
public class Game implements IGame //NOTE: NOT REMOTE
{
    /**
     *
     */
    protected final transient Map<ISession, IClientService> m_Callbacks = Collections.synchronizedMap(new HashMap<ISession, IClientService>());
    /**
     *
     */
    protected final transient List<ISession> m_Players = Collections.synchronizedList(new ArrayList<ISession>());
    /**
     *
     */
    protected transient ISession m_Turn = null;
    /**
     *
     */
    protected transient int m_MaxPlayers;
    /**
     *
     */
    protected transient String m_Name;
    /**
     *
     */
    protected transient int m_State =  GameState.GAME_WAITINGFORPLAYERS;
    /**
     *
     */
    protected transient WorldMap m_Map;
    /**
     *
     */
    protected transient ISession m_Host;
    /**
     *
     */
    protected transient int m_GameTime;
    /**
     *
     */
    protected transient int m_TurnTime = 120; // seconds
    /**
     *
     */
    protected transient javax.swing.Timer m_TurnTimer;
    /**
     *
     */
    protected transient javax.swing.Timer m_GameTimer;
    /**
     *
     */
    protected transient int m_CurrentGameTime;
    /**
     *
     */
    protected transient int m_CurrentTurnTime;
    /**
     *
     */
    protected transient int m_ArmiesPerPlayer = 35;
    /**
     *
     */
    protected transient javax.swing.Timer m_PlacementTimer;
    /**
     *
     */
    protected transient int m_PlacementTime = 60; // 1 min
    /**
     *
     */
    protected transient int m_CurrentPlacementTime = 0;
    /**
     *
     */
    protected transient int m_MaxArmiesPerLand = 4;	// maximum armies / Land
    
    // Aangezien we turn-based spelen is er altijd maar 1 WaitableObject per keer.
    // Mochten we meet nodig hebben dan kunnen we deze listen of mappen.
    /**
     *
     */
    protected transient WaitableObject<Integer> m_WaitableObject = new WaitableObject<Integer>();
    /**
     *
     */
    protected transient GameServer m_Server;
    /**
     *
     */
    protected transient ISession m_WaitingOnObject = null;
    
    /**
     * Geeft de Maximale speeltijd
     * @return speeltijd
     */
    public int getGameTime()
    {
        return m_GameTime;
    }
    
    /**
     * Geeft de Maximale beurttijd
     * @return speeltijd
     */
    public int getTurnTime()
    {
        return m_TurnTime;
    }
    
    /**
     * Constructor
     * @param name naam van de game
     * @param maxplayers maximaal aantal spelers
     * @param map
     * @param gameTime
     * @param turnTime
     * @param armySize
     * @throws RemoteException
     */
    public Game(GameServer server, String name, int maxplayers, WorldMap map, int gameTime, int turnTime, int armySize)
            throws RemoteException
    {
	m_Server = server;
	m_Name = name;
	m_MaxPlayers = maxplayers;
	m_Map = map;
	m_GameTime = gameTime;
	m_TurnTime = turnTime;
	m_ArmiesPerPlayer = armySize;
    }
    
    /**
     * Actionlistener die wordt aangeroepen als waitableObject-Timer 1 seconden geteld heeft
     */
    protected ActionListener waitableObjectListener = new ActionListener()
    {
	public void actionPerformed(ActionEvent e)
	{
	    //NOTE: callbacks are locked
	    if (m_WaitingOnObject != null)
	    {
		try
		{
		    WaitableObjectEvent woe = (WaitableObjectEvent)e;
		    m_Callbacks.get(m_WaitingOnObject).onDefendTimerEvent(woe.getTimeLeft(), woe.getTotalTime());
		}
		catch (Exception ex)
		{
		    ex.printStackTrace();
		}
	    }
	    else
		System.out.println("Error: m_WaitingOnObject == null");
	}
    };
    
    /**
     * Haalt een lijst met alle spelers in deze game op
     * @return spelerlijst
     * @throws RemoteException
     * @throws Exception
     */
    public List<IPlayer> getPlayers() throws RemoteException, Exception
    {
        List<IPlayer> players = new ArrayList<IPlayer>();
        Exception ex = null;
        synchronized(m_Players)
        {
            try
            {
                Iterator<ISession> it = m_Players.iterator();
                while(it.hasNext())
                {
                    ISession session = it.next();
                    players.add(new Player(session));
                }
            }
            catch (Exception e)
            {
                ex = e;
            }
        }
        if (ex != null)
            throw ex;
        else
            return players;
    }

    /**
     * Geeft het maximaal aantal spelers terug dat dit spel mag joinen
     * @return maximaal aantal spelers
     * @throws RemoteException
     */
    public int getMaxPlayers() throws RemoteException
    {
        return m_MaxPlayers;
    }
    
    /**
     * Haalt een (readonly) lijst met alle sessies in deze game op
     * @return sessielijst
     * @throws Exception
     */
    public List<ISession> getSessions() throws Exception
    {
        List<ISession> sessions = new ArrayList<ISession>();
        Exception ex = null;
        synchronized(m_Players)
        {
            try
            {
                Iterator<ISession> it = m_Players.iterator();
                while(it.hasNext())
                {
                    sessions.add(it.next());
                }
            }
            catch (Exception e)
            {
                ex = e;
            }
        }
        if (ex != null)
            throw ex;
        return Collections.unmodifiableList(sessions);
    }
   
    /**
     * Geeft alle geregistreerde callbacks per Client (Sessie) terug.
     * @return 
     */
    public Map<ISession, IClientService> getCallbacks()
    {
        return m_Callbacks;
    }
    
    /**
     * Hiermee kan een speler het spel joinen
     * @param player Ticket object van de speler die de game joined
     * @param callback 
     * @return sessie
     * @throws RemoteException
     * @throws Exception
     */
    public ActionResult<ISession> join(ITicket player, IClientService callback) throws RemoteException, Exception
    {
        // Controleer of spel niet vol zit en mensen mogen joinen
        if (getPlayers().size() == getMaxPlayers())
            return new ActionResult<ISession>(ErrorCodes.ERROR_GAME_FULL, null);
        if (getState() != GameState.GAME_WAITINGFORPLAYERS)
            return new ActionResult<ISession>(ErrorCodes.ERROR_GAME_IN_PROGRESS, null);
        Exception ex = null;
        ISession session = null;
        synchronized(m_Players)
        {
            try
            {
                // Verzamel welke kleuren al in gebruik zijn
                // Dit moet in een loop voor het geval een speler de lobby een keer verlaten heeft.
                List<Integer> usedColors = new ArrayList<Integer>();
                Iterator<ISession> it = m_Players.iterator();
                while (it.hasNext())
                {
                    usedColors.add(it.next().getPlayerColor());
                }
                
                // Kijk welke kleur nog beschikbaar is.
                int color = 0;
                while (usedColors.contains(new Integer(color)))
                    color++;
                
                // Voeg speler toe aan spel
                session = new Session(player.getUsername(), this, color);
                
                // Stel de hostspeler in
                if (getPlayers().isEmpty())
                    m_Host = session;
                
                m_Players.add(session);
            }
            catch (Exception e)
            {
                ex = e;
            }
        }
        if (ex != null)
            throw ex;
        else
        {
            synchronized (m_Callbacks)
            {
		try
		{
		    // Ligt andere clients in:
		    IPlayer plr = new Player(session);
		    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
		    while (it.hasNext())
		    {
			it.next().getValue().onPlayerJoined(plr);
		    }

		    // Registreer deze client
		    m_Callbacks.put(session, callback);
		}
		catch (Exception exc)
		{
		    exc.printStackTrace();
		    ex = exc; 
		}
            }
            return new ActionResult<ISession>(ErrorCodes.ERROR_NOERROR, session);
        }
    }
    
    /**
     * Speler verlaat het spel
     * @param player Speler die het spel verlaat
     * @throws RemoteException
     * @throws Exception
     */
    public void leave(ISession player) throws RemoteException, Exception
    {
	int plrSize = 0;
	Exception ex = null;
	if (m_State == GameState.GAME_PREPARING || m_State == GameState.GAME_SETUP)
	{
	    synchronized (m_Players)
	    {
		try
		{
		    plrSize = m_Players.size() - 1;
		    m_Players.remove(player);
		}
		catch (Exception exc)
		{
		    exc.printStackTrace();
		}
	    }
	
	    synchronized (m_Callbacks)
	    {
		try
		{
		    // Meld deze client af
		    m_Callbacks.remove(player);
		    IPlayer plr = new Player(player);

		    // Licht andere clients in:
		    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
		    while (it.hasNext())
		    {
			try
			{
			    it.next().getValue().onPlayerLeft(plr);
			}
			catch (Exception exc)
			{
			    // Remote exception or Client Exception
			    exc.printStackTrace();
			}
		    }
		}
		catch (Exception exc)
		{
		    ex = exc;
		}
	    }
	    if (ex != null)
		throw ex;
	    
	    // Set new HostPlayer if needed:
	    if (m_Host == player)
	    {
		if (m_Players.size() > 1)
		{
		    Iterator<ISession> it = m_Players.iterator();
		    while (it.hasNext())
		    {
			ISession cur = it.next();
			if (cur != player)
			{
			    m_Host = cur;
			    break;
			}
		    }
		}
		else
		    m_Host = null;      
	    }

	    if (plrSize > 0)
	    {
		// Reset Game (Reinitialize)
		m_State = GameState.GAME_PREPARING;
		if (m_TurnTimer != null)
		    m_TurnTimer.stop();
		if (m_PlacementTimer != null)
		    m_PlacementTimer.stop();
		if (m_GameTimer != null)
		    m_GameTimer.stop();
		m_CurrentTurnTime = m_TurnTime;
		m_CurrentGameTime = m_GameTime;
		m_CurrentPlacementTime = m_PlacementTime;
		start(m_Host);
	    }
	}
	else
	{
	    ISession prevTurnPlr = null;
	    synchronized(m_Players)
	    {
		try
		{
		    giveAwayLands(player);
		    if (m_Turn == player)
		    {
			m_TurnTimer.stop();

			// Calculate next Turn:
			if (m_Players.get(0) == player)
			    prevTurnPlr = m_Players.get(m_Players.size() - 1);
			else
			{
			    Iterator<ISession> it = m_Players.iterator();
			    while (it.hasNext())
			    {
				ISession cur = it.next();
				if (cur == player)
				    break;
				prevTurnPlr = cur;
			    }
			}
		    }

		    // Set new HostPlayer if needed:
		    if (m_Host == player)
		    {
			if (m_Players.size() > 1)
			{
			    Iterator<ISession> it = m_Players.iterator();
			    while (it.hasNext())
			    {
				ISession cur = it.next();
				if (cur != player)
				{
				    m_Host = cur;
				    break;
				}
			    }
			}
			else
			    m_Host = null;
		    }

		    m_Players.remove(player);
		    plrSize = m_Players.size();
		}
		catch (Exception e)
		{
		    ex = e;
		}
	    }
	    if (ex != null)
		throw ex;
	    else
	    {
		synchronized (m_Callbacks)
		{
		    try
		    {
			// Meld deze client af
			m_Callbacks.remove(player);
			IPlayer plr = new Player(player);

			// Ligt andere clients in:
			Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
			while (it.hasNext())
			{
			    try
			    {
				it.next().getValue().onPlayerLeft(plr);
			    }
			    catch (Exception exc)
			    {
				// Remote of Client exception
				exc.printStackTrace();
			    }
			}
		    }
		    catch (Exception exc)
		    {
			ex = exc;
		    }
		}
		
		if (ex != null)
		    throw ex;

		// Speler die aan de beurt was heeft het spel verlaten!
		// Wijzig beurt!
		if (m_TurnTimer != null)
		{
		    if (!m_TurnTimer.isRunning())
		    {
			// Revertback 1 turn then next Turn for not skipping turns
			m_Turn = prevTurnPlr;
			changeTurn(m_Turn);
		    }
		}
	    }
	}
	//if 1 player left stop game
        if(plrSize == 1)
        {
            changeGameState(GameState.GAME_STOPPED);                        
        }
	// Geen Spelers meer?
	if (plrSize == 0)
	{
	    // Sluit spel
	    m_Server.closeGame(this);
	}
    }

    /**
     * Ruimt de gebruikte resources op.
     */
    public void dispose()
    {
	changeGameState(GameState.GAME_STOPPED);
	if (m_GameTimer != null)
	    m_GameTimer.stop();
	if (m_TurnTimer != null)
	    m_TurnTimer.stop();
	if (m_WaitableObject != null)
	    m_WaitableObject.setResult(new Integer(0));
	
	// Clear lists?
	m_Callbacks.clear();
	m_Players.clear();
    }
    
    /**
     * Geeft de huidige GameStatus van de game
     * @return GameStatus
     * @throws RemoteException
     * @throws Exception
     */
    public int getState() throws RemoteException, Exception
    {
        return m_State;
    }

    /**
     * Geeft de naam van het spel terug
     * @return spelnaam
     * @throws RemoteException 
     */
    public String getName() throws RemoteException
    {
        return m_Name;
    }

    /**
     * Geeft de spelinformatie terug van dit spel
     * @return spelinformatie
     * @throws RemoteException 
     */
    public IGameInfo getInfo() throws RemoteException
    {
        return new GameInfo(this);
    }
        
    /**
     * Start het spel
     * @param player speler die de opdracht geeft (host)
     * @return spel gestart successvol
     * @throws RemoteException
     * @throws Exception
     */
    public synchronized boolean start(ISession player)
        throws RemoteException, Exception
    {
        if (m_Host == player && m_State == GameState.GAME_WAITINGFORPLAYERS)
        {
	    // Verander de GameStatus naar preparing:
            changeGameState(GameState.GAME_PREPARING);
            
            // Vraag alle landen van de wereldkaart op:
            List<Land> lands = m_Map.getLands();
            
            // Shuffle landverdeling
            GameServer.shuffleCollection(lands);
            
            // Shuffle Player-Turn-Order
            Exception ex = null;
            synchronized (m_Players)
            {
                try
                {
                    GameServer.shuffleCollection(m_Players);
                    int curId = 0;
                    Iterator<ISession> it1 = m_Players.iterator();
                    while (it1.hasNext())
                    {
			Session session = (Session)it1.next();
                        session.setPlayerId(curId);
			session.setAvailableArmies(m_ArmiesPerPlayer, 0);
			
			// Callback for Available Armies
			Exception ex1 = null;
			synchronized (m_Callbacks)
			{
			    try
			    {
				if (m_Callbacks.containsKey(player))
				    m_Callbacks.get(player).onUpdateAvailableArmies(m_ArmiesPerPlayer, 0);
				else
				    System.out.println("Error: Cannot find player " + player.getName());
			    }
			    catch (Exception exc)
			    {
				ex1 = exc;
			    }
			}
			if (ex1 != null)
			{
			    // RMI-Exception or Map-Exception
			    ex1.printStackTrace();
			}
			
			curId++;
                    }

                    // Verdeel de landen
                    int plrId = 0;
                    Iterator<Land> it2 = lands.iterator();
                    while (it2.hasNext())
                    {
                        try
                        {
                            Session _player = ((Session)m_Players.get(plrId));
                            int landId = it2.next().getId();
                            Land __land = m_Map.getLand(landId);
			    __land.setOwner(_player);
                            __land.setArmies(1);
			    plrId = Math.abs((plrId + 1) % m_Players.size());
                            int armiesLeft =  _player.getAvailableArmies();
			    _player.setAvailableArmies(armiesLeft > 0 ? armiesLeft - 1 : 0, 0);
                            
			    // Ligt alle clients in over het toegewezen land:
                            synchronized (m_Callbacks)
                            {
                                try
                                {
                                    Iterator<Entry<ISession, IClientService>> it3 = m_Callbacks.entrySet().iterator();
                                    while (it3.hasNext())
                                    try
                                    {
                                        // Stuur een land naar een speler toe.
                                        // Aangezien onze landids uniek zijn over all continenten hoeven we slechts
                                        // het landid maar mee te sturen. Anders moest er ook nog continentID bij.
                                        Entry<ISession, IClientService> entry = it3.next();
					entry.getValue().onSetLandOwner(landId, new Player(_player));
					Session session = ((Session)entry.getKey());
					//int armiesLeft = session.getAvailableArmies();
					
					//session.setAvailableArmies(armiesLeft > 0 ? armiesLeft - 1 : 0, 0);
					
					// Callback for Available Armies
					Exception ex1 = null;
					synchronized (m_Callbacks)
					{
					    try
					    {
						if (m_Callbacks.containsKey(player))
						{
                                                    m_Callbacks.get(player).onUpdateAvailableArmies(m_ArmiesPerPlayer, 0);
                                                }
						else
						    System.out.println("Error: Cannot find player " + player.getName());
					    }
					    catch (Exception exc)
					    {
						ex1 = exc;
					    }
					}
					if (ex1 != null)
					{
					    // RMI-Exception or Map-Exception
					    ex1.printStackTrace();
					}
                                    }
                                    catch (Exception exc)
                                    {
                                        // Er ging iets mis met versturen van een server-pakketje
                                        // of de client kon dit niet goed afhandelen.
                                        // Deze fout niet verder afhandelen.
                                        ex.printStackTrace();
                                    }
                                }
                                catch (Exception exc)
                                {
                                    // Onbekende oorzaak, zeldzaam dat deze getriggered wordt.
                                    ex.printStackTrace();
                                }
                            }
                        }
                        catch (Exception exc)
                        {
                            // LandId bestaat niet, zou niet voor moeten komen
                            // Als we hierin komen dan dan zal de exceptie
                            // waarschijnlijk een nullpointerexception zijn.
                            exc.printStackTrace();
                        }
                    }
		    
		    synchronized (m_Callbacks)
		    {
		        // Send Avaialable Amount of Reinforcements
			Iterator<Entry<ISession, IClientService>> it3 = m_Callbacks.entrySet().iterator();
			while (it3.hasNext())
			{
			    Entry<ISession, IClientService> entry = it3.next();
			    entry.getValue().onUpdateAvailableArmies(((Session)entry.getKey()).getAvailableArmies(), 0);
			}
		    }
		}
                catch (Exception exc)
                {
                    ex = exc;
                }
            }
            if (ex != null)
                throw ex;
	    
	    // Set Game Timer
	    if (m_GameTimer != null)
		m_GameTimer.stop();
	    m_CurrentGameTime = 0;
	    m_GameTimer = new javax.swing.Timer(1000, gameTimeListener);
	    m_GameTimer.setRepeats(true);
	    
	    // Set Turn Timer
	    if (m_TurnTimer != null)
		m_TurnTimer.stop();
	    m_CurrentTurnTime = 0;
	    m_TurnTimer = new javax.swing.Timer(1000, turnTimeListener);
	    m_TurnTimer.setRepeats(true);
	    
	    // Set Placement Timer
	    if (m_PlacementTimer != null)
		m_PlacementTimer.stop();
	    m_CurrentPlacementTime = 0;
	    m_PlacementTimer = new javax.swing.Timer(1000, placementTimeListener);
	    m_PlacementTimer.setRepeats(true);

	    // Set Turn
            m_Turn = m_Players.get(Math.abs(GameServer.getRandomNr() % m_Players.size()));
            changeGameState(GameState.GAME_SETUP);
	    m_PlacementTimer.start();
            return true;
        }
        return false;
    }
    
    /**
     * ActionListener voor m_PlacementTimer die ervoor zorgt dat iedereen zijn legers op z'n landen heeft.
     */
    protected ActionListener placementTimeListener = new ActionListener()
    {
	/**
	 * Als deze timer afloopt zullen de resterende legers automatisch worden geplaatst.
	 */
	public void actionPerformed(ActionEvent e)
	{
	    if (m_CurrentPlacementTime < m_PlacementTime)
	    {
		System.out.println("placement timer elapsed: " + m_CurrentPlacementTime + " / " + m_PlacementTime);
		boolean stop = true;
		synchronized (m_Callbacks)
		{
		    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
		    while (it.hasNext())
		    {
			Entry<ISession, IClientService> entry = it.next();
			try
			{
			    System.out.println("available armies: " + entry.getKey().getName() + " has " + entry.getKey().getNumberPlaceableArmys() + " armies left.");
			}
			catch (Exception ex)
			{
			    ex.printStackTrace();
			}
			if (((Session)entry.getKey()).getAvailableArmies() > 0)
			    stop = false;
			try
			{
			    // Notify Timer Event
			    entry.getValue().onPlacementTimerEvent(m_PlacementTime - m_CurrentPlacementTime, m_PlacementTime);
			}
			catch (RemoteException ex)
			{
			    ex.printStackTrace();
			}
			catch (Exception ex)
			{
			    ex.printStackTrace();
			}
		    }
		}
		m_CurrentPlacementTime++;
		
		if (stop)
		{
		    // Start Playing:
		    startPlaying();
		}
	    }
	    else
	    {
		System.out.println("placement timer elapsed: " + m_CurrentPlacementTime + " / " + m_PlacementTime);
		System.out.println("Time's up!");
		
		// Place LeftOvers
		List<Land> allLands = m_Map.getLands();
		synchronized (m_Callbacks)
		{
		    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
		    while (it.hasNext())
		    {
			Entry<ISession, IClientService> entry = it.next();
			Session session = (Session)entry.getKey();
			try
			{
			    System.out.println("Player: " + session.getName() + " still has " + session.getAvailableArmies() + " armies left.");
			}
			catch (Exception _ex)
			{
			    _ex.printStackTrace();
			}
			if (session.getAvailableArmies() > 0)
			{
			    try
			    {
				// Bouw lijst met landen in bezit op
				List<Land> ownedLands = new ArrayList<Land>();
				Iterator<Land> _it = allLands.iterator();
				while (_it.hasNext())
				{
				    Land _land = _it.next();
				    if (_land.getOwner() == session)
					ownedLands.add(_land);
				}
				while (session.getAvailableArmies() > 0)
				{
				    if (ownedLands.isEmpty())
				    {
					// Rarely occurs: chosen army-size was too big
					// (all lands are full).
					System.out.println("placementTimeListener: This Shouldn't suppose to happen.\nAll Player's lands are full or doesn't have any.");
					break;
				    }
				    int idx = Math.abs(GameServer.getRandomNr() % ownedLands.size());
				    Land land = ownedLands.get(idx);
				    int errorCode = reinforce(session, land.getId(), 1);
				    if (errorCode != ErrorCodes.ERROR_NOERROR)
					ownedLands.remove(idx);	// Land zat al vol
				    else
					System.out.println("Player: " + session.getName() + " placed 1 army on " + land.getName() + ".");
				}
			    }
			    catch (RemoteException ex)
			    {
				ex.printStackTrace();
			    }
			    catch (Exception ex)
			    {
				ex.printStackTrace();
			    }
			}
		    }
		}
		
		// Start Playing:
		startPlaying();
	    }
	}
	
	/**
	 * Start het spel zodat er echt gespeeld kan worden.
	 * De initializatie-fase is hiermee voorbij.
	 */
	protected void startPlaying()
	{
	    try
	    {
		// Stop Timer and Start Playing
		m_PlacementTimer.stop();
		changeGameState(GameState.GAME_PLAYING);
                m_MaxArmiesPerLand = Integer.MAX_VALUE;
		m_GameTimer.start();
		
		// Change turn
		changeTurn(m_Turn);
	    }
	    catch (RemoteException ex)
	    {
		ex.printStackTrace();
	    }
	    catch (Exception ex)
	    {
		ex.printStackTrace();
	    }
	}
    };
     
    /**
     * ActionListener voor m_TurnTimer die ervoor zorgt dat als de tijd van een speler's beurt voorbij is
     * zijn beurt wordt beeindigd.
     */
    protected ActionListener turnTimeListener = new ActionListener()
    {
	/**
	 * Stopt het spel als de Speeltijd is afgelopen.
	 */
	public void actionPerformed(ActionEvent e)
	{
	    if (m_CurrentTurnTime >= m_TurnTime)
	    {
		m_TurnTimer.stop();
		Exception ex = null;
		ISession player = null;
		try
		{
                    m_Turn.endTurn();
		    //changeTurn(m_Turn);
		}
		catch (Exception exc)
		{
		    ex.printStackTrace();
		}
	    }
	    else
	    {
		m_CurrentTurnTime++;
	    
		// Callback the Time:
		Exception ex = null;
		synchronized (m_Callbacks)
		{
		    try
		    {
			Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
			while (it.hasNext())
			{
			    try
			    {
				it.next().getValue().onTurnTimerEvent(m_TurnTime - m_CurrentTurnTime, m_TurnTime);
			    }
			    catch (Exception exc)
			    {
				ex.printStackTrace();
			    }
			}
		    }
		    catch (Exception exc)
		    {
			ex = exc;
		    }
		}
		if (ex != null)
		{
		    ex.printStackTrace();
		}
	    }
	};
    };

    /**
     * ActionListener voor m_GameTimer die ervoor zorgt dat als de speeltijd op is het spel wordt beeindigd.
     */
    protected ActionListener gameTimeListener = new ActionListener()
    {
	/**
	 * Stopt het spel als de Speeltijd is afgelopen.
	 */
	public void actionPerformed(ActionEvent e)
	{
	    if (m_CurrentGameTime >= m_GameTime * 60)
	    {
		m_GameTimer.stop();
		// Laat de beurt maar aflopen..
		//changeGameState(GameState.GAME_WAITINGFOREXIT);
	    }
	    else
	    {
		m_CurrentGameTime++;
	    
		// Callback the Time:
		Exception ex = null;
		synchronized (m_Callbacks)
		{
		    try
		    {
			Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
			while (it.hasNext())
			{
			    try
			    {
				it.next().getValue().onGameTimerEvent((m_GameTime * 60) - m_CurrentGameTime, m_GameTime);
			    }
			    catch (Exception exc)
			    {
				ex.printStackTrace();
			    }
			}
		    }
		    catch (Exception exc)
		    {
			ex = exc;
		    }
		}
		if (ex != null)
		{
		    ex.printStackTrace();
		}
	    }
	}
    };
    
    /**
     * Wijzigt de spelstatus en licht de clients in.
     * @param state nieuwe status
     */
    protected void changeGameState(int state)
    {
        m_State = state;

        synchronized (m_Callbacks)
        {
          // Ligt alle clients in:
          Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
          while (it.hasNext())
          {
              try
              {
                 it.next().getValue().onGameStatusChanged(state);
              }
              catch (RemoteException ex)
              {
                  // Remote Exception
                  ex.printStackTrace();
              }
              catch (Exception ex)
              {
                  ex.printStackTrace();
              }
           }
        }
        
        //TODO: if state == GameState.GAME_WAITINGFOREXIT && m_Players.isEmpty()
        //then: meld game af bij gameserver.
    }
    
    /**
     * Stuurt een textbericht naar iedere client
     * @param message textbericht
     * @throws RemoteException
     * @throws Exception
     */
    public void sendGlobalMessage(String message) throws RemoteException, Exception
    {
        Exception ex = null;
        synchronized (m_Callbacks)
        {
            try
            {
                // Ligt alle client in:
                Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
                while (it.hasNext())
                {
                    try
                    {
                        it.next().getValue().onMessage(message);
                    }
                    catch (RemoteException exc)
                    {
                        // Remote Exception
                        exc.printStackTrace();
                    }
                }
            }
            catch (Exception exc)
            {
                ex = exc;
            }
        }
        if (ex != null)
            throw ex;
    }
    
    /**
     * Stuurt een textbericht naar een specifieke client.
     * @param player client
     * @param message textbericht
     */
    protected void sendMessage(ISession player, String message)
    {
        synchronized (m_Callbacks)
        {
              // Ligt de client in:
              try
              {
                    m_Callbacks.get(player).onMessage(message);
              }
              catch (RemoteException ex)
              {
                   // Remote Exception
                   // NOTE: minder belangrijk dus niet verder afhandelen.
                   ex.printStackTrace();
              }
              catch (Exception ex)
              {
                   // Remote Exception
                   // NOTE: minder belangrijk dus niet verder afhandelen.
                   ex.printStackTrace();
              }
        }
    }

    /**
     * Geeft de speler zijn aantal legers waarmee hij zijn landen mag versterken.
     * @param player speler
     */
    protected void updateReinforcements(ISession player)
    {
	int count = 0;
	List<Continent> ownedContinents = new ArrayList<Continent>();
	Iterator<Continent> it = m_Map.getContinents().iterator();
	while (it.hasNext())
	{
	    boolean ownContinent = true;
	    Continent c = it.next();
	    Iterator<Land> _it = c.getLands().iterator();
	    while (_it.hasNext())
	    {
		Land l = _it.next();
		if (l.getOwner() == player)
		    count++;
		else
		    ownContinent = false;
	    }
	    if (ownContinent)
		ownedContinents.add(c);
	}
	
	count /= 3;
	int bonusCount = 0;
	Iterator<Continent> _it = ownedContinents.iterator();
	while (_it.hasNext())
	{
	    bonusCount += _it.next().getBonusValue();
	}
	
	((Session)player).setAvailableArmies(count, bonusCount);
	Exception ex = null;
	synchronized (m_Callbacks)
	{
	    try
	    {
		if (m_Callbacks.containsKey(player))
		{
		    m_Callbacks.get(player).onUpdateAvailableArmies(count, bonusCount);
		}
		else
		    System.out.println("Error: Cannot find player " + player.getName());
	    }
	    catch (Exception exc)
	    {
		ex = exc;
	    }
	}
	if (ex != null)
	{
	    // RMI-Exception or Map-Exception
	    ex.printStackTrace();
	}
    }
    
    public String getGameWinner()
    {
        String winner = "No one";
        int totallands = m_Map.getAmountOfLands();
        ArrayList diffowners = new ArrayList();
        ArrayList A = new ArrayList();
        for(int x = 0; x < m_Players.size(); x++)
        {
            A.add(0);
        }
        
        for(int s = 1; s < totallands+1; s++)
        {
            ISession landowner = m_Map.getLand(s).getOwner();            
            try
            {
                int index = m_Players.indexOf(landowner);
                if(index > 0)
                {
                    int oldvar = Integer.parseInt(A.get(index).toString());
                    A.set(index, oldvar + 1);
                }
            }
            catch(Exception ex)
            {
                String u = ex.getMessage();
            }
            if(!diffowners.contains(landowner))
            {
                diffowners.add(landowner);                
            }
        }
        //count different owners found
        int totaldiff = diffowners.size();
        if(totaldiff == 1)
        {
            //1 player controls all lands, this means he wins
            try
            {
                winner = ((ISession)diffowners.get(0)).getName();
            }
            catch(Exception ex)
            {
                winner = "could not be determined";
            }    
        }    
        else
        {
            //determine the index of the object in arraylist A to get the index of the Session object in m_Players for the Session with the highest amount of lands
            int maxamount = 0;
            int maxpos = 0;
            
            for(int d = 0; d < A.size(); d++)
            {
                int amount = Integer.parseInt(A.get(d).toString());
                if(amount > maxamount)
                {
                    maxamount = amount;
                    maxpos = d;
                }
            }
            try
            {
                winner = m_Players.get(maxpos).getName();
            }
            catch(Exception ex)
            {
                String s = ex.getMessage();
            }
         }
        return winner;
    }
    
    /**
     * Wijzigt de beurt van de spelers.
     * @param curPlayer speler die de beurt wil beeindigen
     * @return errorcode
     * @throws Exception 
     */
    public int changeTurn(ISession curPlayer) throws Exception
    {
	// Check if GameTimer has Expired
	if (!m_GameTimer.isRunning())
	{
	    m_Server.closeGame(this);
	    return ErrorCodes.ERROR_GAME_ENDED;
	}
	else if (m_Turn == null)
	{
	    m_Server.closeGame(this);
	    return ErrorCodes.ERROR_GAME_ENDED;
	}
        
        //----------------------------------------------------------------------------------------------
        //Check if all lands belong to 1 player, this means game ends!
        int totallands = m_Map.getAmountOfLands();
        ArrayList diffowners = new ArrayList();
        for(int s = 1; s < totallands+1; s++)
        {
            ISession landowner = m_Map.getLand(s).getOwner();
            if(!diffowners.contains(landowner))
            {
                diffowners.add(landowner);                
            }
        }
        //count different owners found
        int totaldiff = diffowners.size();
        if(totaldiff == 1)
        {      
            m_Server.closeGame(this);
            //changeGameState(GameState.GAME_STOPPED); 
            return ErrorCodes.ERROR_GAME_ENDED;
        }
	//----------------------------------------------------------------------------------------------
        
        
	ISession realCurPlayer = null;
	synchronized (m_Players)
	{
	    try
	    {
		realCurPlayer = m_Turn;
	    }
	    catch (Exception ex)
	    {
		// Last Player could've left the game.
		if (m_Players.isEmpty())
		    return ErrorCodes.ERROR_NO_ACTIVE_GAME;
		else
		    realCurPlayer = m_Players.get(m_Players.size());
	    }
	}
	
	if (curPlayer != realCurPlayer)
	    return ErrorCodes.ERROR_ITS_NOT_YOUR_TURN; // Invalid Current Player
	
	System.out.println("Turn has been changed!");
	      
	// Set reinforcements
	synchronized (m_Players)
	{
	    if (!m_Players.isEmpty())
	    {
		// update beurt:
		ISession nextTurn = m_Players.get(m_Players.size() - 1); 
		Iterator<ISession> it = m_Players.iterator();
		while (it.hasNext())
		{
		    ISession cur = it.next();
		    if (cur == m_Turn)
			break;
		    nextTurn = cur;
		}
		m_Turn = nextTurn;
	    }
	}
	updateReinforcements(m_Turn);
	
	// Restart Timer
	m_CurrentTurnTime = 0;
	//if (m_TurnTimer.isRunning())
	    m_TurnTimer.restart();
	//else
	//    m_TurnTimer.start();
	
        Exception ex = null;
	IPlayer player = new Player(m_Turn);
        synchronized (m_Callbacks)
        {
            try
            {      
              // Ligt alle clients in:
              Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();  
              while (it.hasNext())
              {
                  try
                  {
		     Entry<ISession, IClientService> entry = it.next();
                     entry.getValue().onGameTurnChanged(player);
                  }
                  catch (RemoteException exc)
                  {
                      // Remote Exception
                      exc.printStackTrace();
                  }
              }
            }
            catch (Exception exc)
            {
                ex = exc;
            }
        }
        if (ex != null)
            throw ex;
	return ErrorCodes.ERROR_NOERROR;
    }

    /**
     * Verplaatst legers van eigenland naar een ander eigenland van een speler.
     * @param eigenLandId eigenland
     * @param anderLandId ander eigenland
     * @param aantalLegers aantal legers
     * @return errorcode
     */
    public boolean move(int eigenLandId, int anderLandId, int aantalLegers)
    {
       // verplaatsen van legers (alleen aangrenzende landen)
       // en alleen als ten minste 1 leger blijft in attackerLand

        Land anderLand = m_Map.getLand(anderLandId);
        Land eigenLand = m_Map.getLand(eigenLandId);
        if (anderLand == null || eigenLand == null)
            return false;   // Failed, want 1 of meer van de landIDs bestaan niet.
        
        // Controleer eigenaren
        if (eigenLand.getOwner() != anderLand.getOwner())
            return false;   // Failed, want eigenaren zijn niet hetzelfde.
            
        if (aantalLegers < 1 && eigenLand.getArmies() - aantalLegers > 0)
            return false;   // Failed, want het aantal legers moet 1 of meer zijn en kleiner dan het aantal op het huidige land - 1.
                
        // Controleer voor alle aangrenzende landen of deze anderLandId zijn.
        List<Integer> grenslanden = eigenLand.getBorderLands();
        if (!grenslanden.contains(anderLandId))
            return false;   // Failed: landen zijn niet aangrenzend
        
        try
        {
            int aantaleigen = eigenLand.getArmies();
            int aantalander = anderLand.getArmies();
            eigenLand.setArmies(aantaleigen - aantalLegers);
            anderLand.setArmies(aantalander + aantalLegers);

            Exception ex = null;
            synchronized (m_Callbacks)
            {
                try
                {
                    // Ligt alle client in:
                    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
                    while (it.hasNext())
                    {
                        it.next().getValue().onMoveArmies(aantalLegers, eigenLandId, anderLandId);
                    }
                }
                catch (Exception exc)
                {
                    ex.printStackTrace();
                    ex = exc;
                }
            }
            if (ex != null)
                throw ex;

            sendGlobalMessage(eigenLand.getOwner().getName() + " heeft " + aantalLegers + " van " + eigenLand.getName() + " naar " + anderLand.getName() + " verplaatst.");
        }
        catch (Exception ex)
        {
             ex.printStackTrace();
        }
        return true;    // Gelukt; legers verplaatst
    }
 
    /**
     * Als de een speler een besluit heeft gemaakt wat hij in de beurt wil doen geeft deze methode
     * dit door aan alle andere clients.
     * @param session
     * @param decission
     * @return errorcode
     * @throws RemoteException
     * @throws Exception 
     */
    public int turnDecission(ISession session, int decission) throws RemoteException, Exception
    {
	if (isTurnOf(session))
	{
	    if (decission == TurnDecission.ReinforceBonus)
	    {
		// Remove Available Armies, except BonusArmies:
		((Session)session).setAvailableArmies(0, ((Session)session).getAvailableBonusArmies());
	    }
	    
	    IPlayer player = new Player(session);
	    Exception ex = null;
	    synchronized (m_Callbacks)
	    {
		try
		{
		    Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
		    while (it.hasNext())
		    {
			try
			{
			    Entry<ISession, IClientService> entry = it.next();
			    if (entry.getKey() != session)
				entry.getValue().onPlayerDecided(player, decission);
			}
			catch (Exception exc)
			{
			    exc.printStackTrace();
			}
		    }
		}
		catch (Exception exc)
		{
		    ex = exc;
		}
	    }

	    if (ex != null)
		throw ex;
	    return ErrorCodes.ERROR_NOERROR;
	}
	return ErrorCodes.ERROR_ITS_NOT_YOUR_TURN;
    }
    
    /**
     * Controleert of de speler aan de beurt is.
     * @param session speler
     * @return aan de beurt of niet
     * @throws Exception
     */
    protected boolean isTurnOf(ISession session) throws Exception
    {
	Exception ex = null;
	boolean isTurn = false;
	synchronized (m_Players)
	{
	    try
	    {
		isTurn = m_Turn == session;
	    }
	    catch (Exception exc)
	    {
		ex = exc;
	    }
	}
	if (ex != null)
	    throw ex;
	return isTurn;
    }
    
    /**
     * Geeft de mogelijkheid voor een speler om zijn landen te versterken.
     * @param session speler
     * @param landID landnr
     * @param amount aantal
     * @return errorcode
     * @throws RemoteException
     * @throws Exception
     */
    public int reinforce(ISession session, int landID, int amount) throws RemoteException, Exception
    {
	if (m_State != GameState.GAME_SETUP)
	{
	    try
	    {
		if (!isTurnOf(session))
		    return ErrorCodes.ERROR_ITS_NOT_YOUR_TURN;
	    }
	    catch (Exception ex)
	    {
		return ErrorCodes.ERROR_UNKNOWN;
	    }
	}
	
	// We May Reinforce
	Land land = m_Map.getLand(landID);
	if (land == null)
	    return ErrorCodes.ERROR_INVALID_LANDID;
	
	if (land.getOwner() != session)
	    return ErrorCodes.ERROR_LAND_IS_NOT_YOURS;
	
	if (amount + land.getArmies() > m_MaxArmiesPerLand)
	    return ErrorCodes.ERROR_LAND_IS_FULL;
	
	int available = ((Session)session).getAvailableArmies();
	int availableBonus = ((Session)session).getAvailableBonusArmies();
	if (amount > available + availableBonus)
	    return ErrorCodes.ERROR_NOT_ENOUGH_ARMIES;
	
	int oldArmySize = land.getArmies();
	int newArmySize = oldArmySize + amount;
	land.setArmies(newArmySize);
	
	if (amount > available)
	{
	    int temp = amount - available;
	    available = 0;
	    availableBonus -= temp;
	}
	else
	    available -= amount;
	((Session)session).setAvailableArmies(available, availableBonus);
	
	// Callback
	IPlayer player = new Player(session);
	Exception ex = null;
	synchronized (m_Callbacks)
	{
	    try
	    {
		Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
		while (it.hasNext())
		{
		    try
		    {
			it.next().getValue().onArmiesPlaced(player, landID, newArmySize, newArmySize - oldArmySize);
		    }
		    catch (Exception exc)
		    {
			exc.printStackTrace();
		    }
		}
	    }
	    catch (Exception exc)
	    {
		ex = exc;
	    }
	}
	if (ex != null)
	    throw ex;
	
	// Update Reinforcements
	synchronized (m_Callbacks)
	{
	    try
	    {
		m_Callbacks.get(session).onUpdateAvailableArmies(available, availableBonus);
	    }
	    catch (Exception exc)
	    {
		ex = exc;
	    }
	}
	if (ex != null)
	    throw ex;
	return ErrorCodes.ERROR_NOERROR;
    }
    
    /**
     * Plaatst een bepaalde hoeveelheid legers op een land erbij.
     * @param session sessie die de actie uitvoert
     * @param landID landid dat eigendom is van de sessie waarop geplaatst wordt
     * @param armies aantal legers (positief of negatief)
     * @return errorcode
     * @throws RemoteException
     * @throws Exception 
     */
    public int placeArmies(Session session, int landID, int armies) throws RemoteException, Exception
    {
	Land land = m_Map.getLand(landID);
	int oldArmies = land.getArmies();

	if ((armies < oldArmies && armies < 0) || (armies > oldArmies && armies > 0))
	    return ErrorCodes.ERROR_TOO_MANY_ARMIES;

	if (land.getOwner() != session)
	    return ErrorCodes.ERROR_LAND_IS_NOT_YOURS;

	int oldArmySize = land.getArmies();
	int newArmySize = oldArmySize + armies;
	land.setArmies(newArmySize);
	
	IPlayer player = new Player(session);
	Exception ex = null;
	synchronized (m_Callbacks)
	{
	    try
	    {
		Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
		while (it.hasNext())
		{
		    try
		    {
			it.next().getValue().onArmiesPlaced(player, landID, newArmySize, newArmySize - oldArmySize);
		    }
		    catch (Exception exc)
		    {
			// Client or RMI Error
			exc.printStackTrace();
		    }
		}
	    }
	    catch (Exception exc)
	    {
		ex = exc;
	    }
	    if (ex != null)
		throw ex;
	}
	return ErrorCodes.ERROR_NOERROR;
    }
    
    /**
     * Deze methode roept de client aan als hij een land wil gaan aanvallen.
     * @param session speler
     * @param landID1 eigenland
     * @param amount aantal
     * @param landID2 anderland
     * @return errorcode
     * @throws RemoteException
     * @throws Exception
     */
    public int attack(ISession session, int landID1, int landID2, int amount) throws RemoteException, Exception
    {
	// Pause the Timer
	m_TurnTimer.stop();
	
	// Constraints
	Land defenderLand = m_Map.getLand(landID2);
        Land attackerLand = m_Map.getLand(landID1);
        if (defenderLand == null || attackerLand == null)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
            return ErrorCodes.ERROR_INVALID_LANDID;   // Failed, want 1 of meer van de landIDs bestaan niet.
	}
        
	//@flow [CHANGED] landGeef aanvals sterkte op (= aantalLegers uit aanroep methode)0 < aanvalsterker < aantallandlegers - 1?
        if (amount < 1 && attackerLand.getArmies() - amount > 0 && amount < m_MaxArmiesPerLand)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
            return ErrorCodes.ERROR_INVALID_ARMY_COUNT;   // Failed, want het aantal legers moet 1 of meer zijn en kleiner dan het aantal op het huidige land - 1.
	}
        
	if (attackerLand.getOwner() != session)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
	    return ErrorCodes.ERROR_LAND_IS_NOT_YOURS;   // Failed, want dit land is niet in bezig van de juiste eigenaar.
	}
	
	if (!isTurnOf(session))
	{
	    // Resume the Timer
	    m_TurnTimer.start();
	    return ErrorCodes.ERROR_ITS_NOT_YOUR_TURN;   // Failed, want deze speler is niet aan de beurt.
	}
	
        if (attackerLand.getOwner() == defenderLand.getOwner())
	{
	    // Resume the Timer
	    m_TurnTimer.start();
            return ErrorCodes.ERROR_CANNOT_ATTACK_OWN_LANDS;   // Failed, beiden landen zijn van dezelfde eigenaar.
	}

        // Controleer voor alle aangrenzende landen of deze anderLandId zijn.
        // @flow Zijn Landen aangrenzend ?
        List<Integer> grenslanden = attackerLand.getBorderLands();
        if (!grenslanden.contains(landID2))
	{
	    // Resume the Timer
	    m_TurnTimer.start();
            return ErrorCodes.ERROR_LAND_IS_TOO_FAR_AWAY;
	}
	
	// All Constraints have resulted Valid, Continue..
	
	// Ask Defender with how many he wants to defend.
	IPlayer attackingPlr = new Player(attackerLand.getOwner());
	IPlayer defendingPlr = new Player(defenderLand.getOwner());
	int defAmount = defenderLand.getArmies() >= 2 ? 2 : defenderLand.getArmies();
	Exception ex = null;
	int result = ErrorCodes.ERROR_NOERROR;
	synchronized (m_Callbacks)
	{
	    try
	    {
		IClientService callback = m_Callbacks.get(defenderLand.getOwner());
		if (callback == null)
		    result = ErrorCodes.ERROR_OTHER_PLAYER_NOT_FOUND;
		else
		{
		    // Notify Defender:
		    callback.onDefending(attackingPlr, landID1, amount, landID2, defAmount);
		    
		    // Open Dialog in Client defenderLand (Asynchronous):
		    if (m_WaitingOnObject != null) 
		    {
			    System.out.println("Cannot wait with Waitable Object (in use).");
			m_WaitingOnObject = null;//result = ErrorCodes.ERROR_UNKNOWN;	// Can't wait for some reason..
		    }
		    else
		    {
			m_WaitingOnObject = defenderLand.getOwner();
			if (!m_WaitableObject.wait(15, waitableObjectListener))	// 15 sec blocked waiting
			{
			    System.out.println("Cannot wait with Waitable Object (unknown reason).");
			   m_WaitingOnObject = null;// result = ErrorCodes.ERROR_UNKNOWN;	// Can't wait for some reason..
			}
			else
			{
			    Integer woResult = m_WaitableObject.getResult();
			    if (woResult != null)
				defAmount = woResult.intValue();
			    System.out.println("Received Defending Army amount: " + defAmount);
			}
		    }
		}
	    }
	    catch (Exception exc)
	    {
		m_WaitingOnObject = null;//result = ErrorCodes.ERROR_UNKNOWN;
		ex = exc;
	    }
	}

	m_WaitingOnObject = null;
	if (ex != null)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
	    throw ex;
	}
	if (result != ErrorCodes.ERROR_NOERROR)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
	    return result;
	}
	
	// Validate Defender Army Size:
	if (defAmount > 2 || defAmount <= 0 || defAmount > defenderLand.getArmies())
	    defAmount = defenderLand.getArmies() >= 2 ? 2 : defenderLand.getArmies();
	
	// Roll the Dices:
	List<Integer> defDices = new ArrayList<Integer>();
	for (int t = 0; t < defAmount; t++)
        {
	    defDices.add(Math.abs(GameServer.getRandomNr() % 6) + 1);
        }

	Collections.sort(defDices,Collections.reverseOrder());
	List<Integer> atDices = new ArrayList<Integer>();
	for (int t = 0; t < amount; t++)
        {
            atDices.add(Math.abs(GameServer.getRandomNr() % 6) + 1);
        }
	    
	Collections.sort(atDices,Collections.reverseOrder());
	
	// Calculate lost armies (in favor of the Defender):
	int count = amount > defAmount ? defAmount : amount;
	int lostDefArmies = 0;
	int lostAttArmies = 0;
	Iterator<Integer> dit = defDices.iterator();
	Iterator<Integer> ait = atDices.iterator();
	for (int t = 0; t < count; t++)
	{
	   Integer def = dit.next();
	   Integer att = ait.next();
          
	   if (def >= att)
	       lostAttArmies++;
	   else
	       lostDefArmies++;
	}
	// Update Lands (without callback)
        
	if (defenderLand.getArmies() - lostDefArmies <= 0)
	{
	    // Land is conquered by session
	    defenderLand.setOwner(session);
	    defenderLand.setArmies(amount);
	    attackerLand.setArmies(attackerLand.getArmies() - amount);
	}
	else
	{
	    // Defender resisted the Attack
	    defenderLand.setArmies(defenderLand.getArmies() - lostDefArmies);
	    attackerLand.setArmies(attackerLand.getArmies() - lostAttArmies);
            System.out.println("verdediger legers " + defenderLand.getArmies()+ " attacker "+ attackerLand.getArmies());
	}
	
	// Show everyone the attack-result
	synchronized (m_Callbacks)
        {
            try
            {
                // Ligt alle clients in:
                Iterator<Entry<ISession, IClientService>> it = m_Callbacks.entrySet().iterator();
                while (it.hasNext())
                {
                    try
                    {
                        it.next().getValue().onAttack(attackingPlr, attackerLand.getId(), atDices, lostAttArmies, defendingPlr, defenderLand.getId(), defDices, lostDefArmies);
                    }
                    catch (RemoteException exc)
                    {
                        // Remote Exception
                        exc.printStackTrace();
                    }
                    catch (Exception exc)
                    {
                        // Remote Exception
                        exc.printStackTrace();
                    }
                }
            }
            catch (Exception exc)
            {
                ex = exc;
            }
        }
        
        if (ex != null)
	{
	    // Resume the Timer
	    m_TurnTimer.start();
	    m_WaitingOnObject = null;//result = ErrorCodes.ERROR_UNKNOWN;
            throw ex;
	}

	// Resume the Timer
	m_TurnTimer.start();
	m_WaitingOnObject = null;
        return result;
    }

    /**
     * Roept de speler op als hij zijn verdediging wil doorgeven.
     * @param session speler (verdediger)
     * @param amount aantal legers
     * @return errorcode
     * @throws RemoteException
     * @throws Exception 
     */
    public int defend(ISession session, int amount) throws RemoteException, Exception
    {
	if (m_WaitingOnObject != session)
	    return ErrorCodes.ERROR_ITS_NOT_YOUR_TURN;
	
	if (m_WaitableObject.setResult(amount))
	    return ErrorCodes.ERROR_NOERROR;
	return ErrorCodes.ERROR_DEFEND_AMOUNT_ALREADY_SET;
    }

    /**
     * Geeft de landen van een speler weg die het spel wil verlaten of het spel al verlaten is.
     * @param session verlatende speler-sessie
     * @throws Exception
     */
    protected void giveAwayLands(ISession session) throws Exception
    {
	//WARNING: only call within a synchronized (m_Players)-block.
	System.out.println("giving away lands..");
	Exception ex = null;
	try
	{
	    // Copy list
	    List<ISession> playersCopy = new ArrayList<ISession>(m_Players);
	    playersCopy.remove(session);
		
	    if (playersCopy.size() > 0)
	    {
		// Shuffle PlayerList
		GameServer.shuffleCollection(playersCopy);

		// Set first player
		int plrIdx = 0;

		// Devide Lands from Player 'session'.
		Iterator<Land> it = m_Map.getLands().iterator();
		while (it.hasNext())
		{
		    Land land = it.next();
		    if (land.getOwner() == session)
		    {
			// Next Player (which isn't 'session')
			ISession player = playersCopy.get(plrIdx);
			plrIdx = Math.abs((plrIdx + 1 ) % playersCopy.size());
			land.setOwner(player);
                        land.setArmies(1);
			System.out.println("Player: " + player.getName() + " received land: " + land.getName());

			// Nofity Clients
			IPlayer owner = new Player(player);
			synchronized (m_Callbacks)
			{
			    try
			    {
				// Ligt alle clients in:
				Iterator<Entry<ISession, IClientService>> __it = m_Callbacks.entrySet().iterator();
				while (__it.hasNext())
				{
				    try
				    {
					Entry<ISession, IClientService> entry = __it.next();
					if (entry.getKey() != session)
					    entry.getValue().onSetLandOwner(land.getId(), owner);
				    }
				    catch (RemoteException exc)
				    {
					// Remote Exception
					exc.printStackTrace();
				    }
				    catch (Exception exc)
				    {
					// Remote Exception
					exc.printStackTrace();
				    }
				}
			    }
			    catch (Exception exc)
			    {
				ex = exc;
			    }
			}
		    }
		}
	    }
	}
	catch (Exception exc)
	{
	    ex = exc;
	}
	if (ex != null)
	    throw ex;
    }
}
